package peace.grapher
{
import flash.display.DisplayObject;
import flash.display.Sprite;
import flash.geom.Point;
import flash.text.TextFormat;

import peace.data.Interval;
import peace.graphic.Painter;
/**
 * Ось с метками и направлением 
 * 
 */
public class Axis extends Sprite
{
	/**
	 * 
	 */	
	public var isHorizontal:Boolean = true;
	/**
	 * 
	 */	
	public var interval:Interval = new Interval(0, 10);
	/**
	 * размер (ширина или высота в зависимости от isHorizontal)
	 */	
	public var size:Number = 300;
	/**
	 * Текстовая метка оси
	 */
	public var labelInstance:IAxisLabel = new AxisLabel(0, 1, new TextFormat ("arial", 10, color));
	/**
	 * шаг с большой палочкой и текстовой меткой 
	 */
	public var step:Number = 1;
	/**
	 * шаг со средней палочкой (step должен быть кратен ему)
	 */
	public var middleStep:Number = 0.5;
	/**
	 * шаг с минимальной палочкой (step должен быть кратен ему)
	 */
	public var minStep:Number = 0.1;
	/**
	 * при изменении размера шаг подстраивается, <br> 
	 * чтобы сохранить постоянное расстояние между минимальными рисками
	 * 
	 * @default true
	 */
	public var stepIsDynamic:Boolean = true;
	/**
	 * Eсли stepIsDynamic, ограничение для шага оси снизу. <br>
	 * Работает только если != 0
	 * 
	 * @default 0
	 */
	public var minDynamicStep:Number = 0;
	/**
	 * Eсли stepIsDynamic, ограничение для шага оси сверху. <br>
	 * Работает только если != 0
	 * 
	 * @default 0
	 */
	public var maxDynamicStep:Number = 0;
	/**
	 * размер (ширина или высота в зависимости от isHorizontal) стрелки в конце, <br>
	 * не влияет на size
	 */	
	public var arrowSize:Number = 25;

	////////////////////////////////////////////
	// настройки представления
	////////////////////////////////////////////
	/**
	 * 
	 */
	public var color:Number = 0x000000;
	/**
	 * 
	 */
	public var lineThickness:int = 1;
	/**
	 * 
	 */
	public var lineAlpha:Number = 1;
	/**
	 * 
	 */
	public var smallMarkHeight:Number = 3;
	/**
	 * 
	 */
	public var middleMarkHeight:Number = 6;
	/**
	 * 
	 */	
	public var bigMarkHeight:Number = 8;
	/**
	 * 
	 */
//	public var hasBeginArrow:Boolean = false;
	/**
	 * 
	 */
	public var hasEndArrow:Boolean = true;
	/**
	 * добавляем ли метку 0 в нуле
	 */
	public var hasNil:Boolean = true;
	/**
	 * координаты больших меток <br>
	 * используются для дальнейшей отрисовки сетки в CoordinatePlane
	 */	
	public var bigmarks:Array = [];
	/**
	 * расстояние между минимальными палками
	 */
	protected var minStepPixels:Number;
	/**
	 * 
	 * @param isHorizontal
	 * @param interval
	 * @param size
	 * @param labelInstance
	 * 
	 */
	public function Axis (	isHorizontal:Boolean = true, 
							interval:Interval = null, 
							size:Number = NaN, 
							labelInstance:IAxisLabel = null)
	{
		super();
		this.isHorizontal = isHorizontal;
		if (interval) this.interval = interval;
		if (size) this.size = size;
		if (labelInstance) this.labelInstance = labelInstance;
		
		mouseEnabled = false;
 	}
	/**
	 * Основной инструмент отрисовки 
	 * Перерисовка оси с новыми параметрами 
	 * 
	 */	
	public function redraw ():void
	{
		if (stepIsDynamic)
		{
			tuneMinStep();
		}
		
		// трем графику
		graphics.clear();
		
		// чистим массив координат меток
		bigmarks.length = 0;
		
		// трем метки
		for (;numChildren > 0;)
		{
			removeChildAt(0);
		}
		
		// рисуем
		graphics.lineStyle(lineThickness, color, lineAlpha, true, "normal", "none");
		
		var i:Number, g:Number, mh:Number;
		var l:DisplayObject;
		for (i = Math.ceil(interval.min/minStep) * minStep; i <= interval.max + minStep/2; i += minStep)
		{
			g = Math.round (getGlobalValue(i));
			mh = getMarkHeightByValue (i);
			
			// рисуем рисочки
			if (isHorizontal)
			{
				graphics.moveTo (g, int(lineThickness/2));
				graphics.lineTo (g, mh);
			}
			else
			{
				graphics.moveTo (- int(lineThickness/2), g);
				graphics.lineTo (- mh, g);
			}
			
			// расставляем метки
			if (mh == bigMarkHeight)
			{
				// сохраняем метку
				bigmarks.push(g);
				
				if (Math.abs(i) < minStep/2 && ! hasNil)
				{
					continue;
				}

				l = DisplayObject (createLabel(i));
				addChild(l);
				if (isHorizontal)
				{
					l.x = g - l.width/2;
					l.y = bigMarkHeight - 2;
				}
				else
				{
					l.x = - bigMarkHeight - l.width;
					l.y = g - l.height/2;
				}
			}
		}
		
		// рисуем стрелочку
		if (hasEndArrow)
		{
			graphics.beginFill(color, lineAlpha);
			if (isHorizontal) Painter.arrow(graphics, new Point(0,0), new Point(size + arrowSize, 0), 12, 4);
			else Painter.arrow(graphics, new Point(0,size), new Point(0,- arrowSize), 12, 4);
		}
		else
		{
			graphics.moveTo(0,0);
			if (isHorizontal) graphics.lineTo(size, 0)
			else graphics.lineTo(0, size);
		}
	}
	/**
	 * 
	 * @param globalCoordinate глобальная координата
	 * @return 
	 * 
	 */	
	public function getLocalValue (globalCoordinate:Number):Number
	{
		return isHorizontal ? interval.min + globalCoordinate/size * interval.length 
							: interval.min + (1 - globalCoordinate/size) * interval.length;
	}
	/**
	 * 
	 * @param localCoordinate локальная координата
	 * @return 
	 * 
	 */	
	public function getGlobalValue (localCoordinate:Number):Number
	{
		return isHorizontal ? (localCoordinate - interval.min) * size / interval.length
							: (interval.max - localCoordinate) * size / interval.length;
	}
	/**
	 * 
	 * @param value
	 * 
	 */	
	override public function set width(value:Number):void
	{
		if (isHorizontal)
		{
			size = value;
		}
	}
	/**
	 * 
	 * @param value
	 * 
	 */	
	override public function set height(value:Number):void
	{
		if (!isHorizontal)
		{
			size = value;
		}
	}
	/**
	 * 
	 * @return 
	 * 
	 */	
	override public function get width():Number 
	{
		return isHorizontal ? size : super.width
	}
	/**
	 * 
	 * @return 
	 * 
	 */	
	override public function get height():Number 
	{
		return !isHorizontal ? size : super.height
	}
	/**
	 * 
	 * @return 
	 * 
	 */	
	public function get realWidth ():Number
	{
		return super.width;
	}
	/**
	 * 
	 * @return 
	 * 
	 */	
	public function get realHeight ():Number
	{
		return super.height;
	}
	/**
	 * Расходник при отрисовке палочек 
	 * 
	 * @param n
	 * @return 
	 * 
	 */	
	protected function getMarkHeightByValue (n:Number):Number
	{
		n = Math.abs (n);
		
		if (n % step < minStep/2 || n % step > step - minStep/2)
		{
			return bigMarkHeight;
		}
		else if (n % middleStep < minStep/2 || n % middleStep > middleStep - minStep/2)
		{
			return middleMarkHeight;
		}
		return smallMarkHeight;
	}
	/**
	 * Для stepIsDynamic настраиваем оптимальный шаг для переданного размера
	 *  
	 * @param w
	 * 
	 */	
	protected function tuneMinStep ():void
	{
		minStepPixels = size / (interval.length / minStep);
		
		// удваиваем шаг
		if (minStepPixels < 3)
		{
			if (maxDynamicStep > 0 && maxDynamicStep < step*2) return;
			
			step *= 2;
			middleStep *= 2;
			minStep *= 2;
			tuneMinStep();
		}
		
		// делим шаг на двое
		else if (minStepPixels > 6)
		{
			if (minDynamicStep > 0 && minDynamicStep > step/2) return;
			
			step /= 2;
			middleStep /= 2;
			minStep /= 2;
			tuneMinStep();
		}
	}
	/**
	 * 
	 * @param n
	 * @return 
	 * 
	 */
	protected function createLabel (n:Number):IAxisLabel
	{
		// если метка по умолчанию, то подгоняем ноль
		if (labelInstance is AxisLabel && AxisLabel(labelInstance).precision > 0)
		{
			if (n > -minStep/2 && n < minStep/2)
			{
				n = 0;
			}
		}
		
		return labelInstance.clone(n);
	}
	
}
}


